POSIX IPC와 System V IPC 비교 — 메시지 큐 중심

NOTE

공유메모리 쪽의 POSIX/System V 비교는 이미 공유메모리 세그먼트 노트에 있다. 이 노트는 비어 있던 메시지 큐 비교를 채운다. 검증 표기: ✔ Linux man 페이지(man7.org)에서 확인, △ 기억/일반 지식.

0. 두 계보

System V IPCPOSIX IPC
계보AT&T System V 유닉스 (1980년대). 전통 UNIX(HP-UX, Solaris, AIX)에 널리 있음POSIX.1b(실시간 확장)로 표준화된 더 새로운 인터페이스
식별 방법숫자 key (ftok 등)이름 문자열 ("/name")
핸들msqid 같은 정수 ID(파일 디스크립터가 아님)mqd_t (Linux에서는 파일 디스크립터)
가시화ipcs, ipcrm/dev/mqueue를 마운트하면 ls, rm

세 가지 자원이 각각 대응된다. 공유메모리와 세마포어는 기존 노트로 연결한다.

자원System VPOSIX기존 노트
메시지 큐msgget/msgsnd/msgrcv/msgctlmq_open/mq_send/mq_receive/mq_close/mq_unlink이 노트
공유메모리shmget/shmat/shmdt/shmctlshm_open+ftruncate+mmap/munmap/shm_unlink세그먼트, mmap
세마포어semget/semop/semctlsem_open/sem_wait/sem_post세마포어

1. 메시지 큐 함수 대응표

동작System VPOSIX
만들기/열기msgget(key, msgflg) → msqidmq_open(name, oflag, mode, &attr) → mqd_t
보내기msgsnd(msqid, msgp, msgsz, msgflg)mq_send(mqdes, msg_ptr, msg_len, msg_prio), mq_timedsend
받기msgrcv(msqid, msgp, msgsz, msgtyp, msgflg)mq_receive(mqdes, msg_ptr, msg_len, &msg_prio), mq_timedreceive
속성/상태 조회msgctl(msqid, IPC_STAT, &ds) → struct msqid_dsmq_getattr(mqdes, &attr) → struct mq_attr
속성 변경msgctl(msqid, IPC_SET, &ds)mq_setattr (사실상 O_NONBLOCK만) △
닫기(핸들 개념 없음, 아무것도 안 함)mq_close(mqdes)
삭제msgctl(msqid, IPC_RMID, NULL)mq_unlink(name)
도착 알림없음mq_notify — 빈 큐에 메시지가 도착할 때 시그널/스레드로 통지 ✔

1-1. 메시지 형식

/* System V: 앞에 long 타입 필드가 반드시 있어야 한다 */
struct my_msg {
    long mtype;          /* 반드시 0보다 커야 한다 ✔ */
    char mtext[256];
};
msgsnd(msqid, &m, sizeof(m.mtext), 0);          /* 크기에는 mtype을 포함하지 않는다 */
msgrcv(msqid, &m, sizeof(m.mtext), 0, 0);       /* msgtyp=0 → 맨 앞 메시지 */
/* POSIX: 그냥 바이트 덩어리 + 우선순위 */
char buf[8192];
unsigned prio;
mq_send(mqd, "hello", 5, /*prio*/ 1);
ssize_t n = mq_receive(mqd, buf, sizeof(buf), &prio);   /* buf는 mq_msgsize 이상이어야 한다 */

1-2. 받을 메시지를 고르는 방법 — 가장 다른 점 ✔

System VPOSIX
기준msgtyp(메시지 타입 번호)우선순위(prio)
규칙0: 맨 앞 메시지
>0: 그 타입의 첫 메시지 (MSG_EXCEPT면 그 타입이 아닌 첫 메시지)
<0: 타입 ≤ |msgtyp|인 것 중 가장 낮은 타입
우선순위가 가장 높은 메시지부터 항상 먼저 전달. Linux에서 범위 0~32767
쓰임새한 큐를 여러 수신자가 공유하며 각자 자기 타입만 골라 받을 수 있다 (mtype을 수신자 번호로)긴급 메시지를 앞지르기

System V의 mtype은 "수신자 주소"로 많이 쓰인다

큐 하나에 mtype = 수신자 PID를 붙여 보내고, 각 프로세스는 msgrcv(..., getpid(), ...)로 자기 것만 받는 설계가 가능하다. POSIX 큐에는 이런 선택 수신이 없어서 수신자마다 큐를 따로 만들어야 한다.

1-3. 블로킹과 타임아웃

System VPOSIX
기본 동작보낼 때 공간이 없으면 블록, 받을 때 메시지가 없으면 블록 ✔동일 (블록)
논블로킹IPC_NOWAIT → 송신 EAGAIN, 수신 ENOMSG ✔O_NONBLOCK로 열면 EAGAIN
타임아웃 지정없다. 필요하면 alarm/시그널, 폴링으로 흉내mq_timedsend/mq_timedreceive로 절대 시각 지정 △
시그널에 의한 중단msgrcv는 SA_RESTART와 무관하게 재시작되지 않는다 → EINTR를 직접 처리해야 한다 ✔EINTR로 중단됨 △

1-4. 이벤트 루프(select/poll/epoll)에 넣을 수 있는가 — 구조를 갈라놓는 차이

System V 메시지 큐POSIX 메시지 큐
핸들msqid는 파일 디스크립터가 아니다Linux에서 “메시지 큐 디스크립터는 실제로 파일 디스크립터”이며 select/poll/epoll로 감시할 수 있다 ✔ (단, “이식 불가”)
의미epoll로 “큐에 메시지가 왔는지” 기다릴 수 없다 △소켓과 같이 하나의 epoll 루프에서 기다릴 수 있다

그래서 System V 메시지 큐를 쓰는 서버는 보통 이런 구조가 된다.

flowchart LR
    subgraph proc["서버 프로세스"]
        EP["이벤트 스레드\nepoll_wait(소켓들)"]
        RX["IPC 수신 스레드\nmsgrcv() 블록"]
        Q["스레드 간 큐"]
        W["처리 스레드들"]
    end
    SOCK["네트워크 소켓"] --> EP --> Q
    MQ[("System V 메시지 큐")] -->|"msgrcv (블록)"| RX --> Q
    Q --> W

소켓은 epoll로, 메시지 큐는 별도 스레드가 msgrcv로 블록 하는 이원 구조다. 큐가 POSIX였다면 epoll 하나로 합칠 수 있다. System V 큐를 쓰는 레거시 서버 구조에서 “이벤트 스레드와 IPC 수신 스레드가 따로 있는 이유”를 이 차이로 설명할 수 있다. △ (일반적인 설계 추정이며, 개별 제품에서 실제로 그런지는 해당 구현을 확인해야 한다)

2. 한도와 크기 — 어디서 막히는가

2-1. System V

한도sysctl의미확인
MSGMNIkernel.msgmni시스템 전체 큐 개수 상한. Linux 3.19부터 기본 32,000. 초과 시 msgget이 ENOSPC ✔cat /proc/sys/kernel/msgmni
MSGMAXkernel.msgmax메시지 하나의 최대 크기 △cat /proc/sys/kernel/msgmax
MSGMNBkernel.msgmnb큐 하나에 쌓을 수 있는 총 바이트. 큐를 만들 때 msg_qbytes가 이 값으로 설정된다 ✔cat /proc/sys/kernel/msgmnb
  • msg_qbytes는 msgctl(IPC_SET)으로 큐마다 따로 올릴 수 있다(권한 필요) △. 따라서 ipcs -q -l의 시스템 한도와, 큐 자체의 msg_qbytes가 다를 수 있다.
  • 메시지가 MSGMAX보다 크면 msgsnd가 EINVAL로 실패한다 △. 큰 데이터는 조각내서 보내야 한다.
  • 기본값은 배포판과 커널 버전에 따라 다르다. 리눅스 커널 소스의 기본 MSGMAX는 8,192지만 배포판이 더 크게 잡는 경우가 많다 △. 직접 확인한 예: 한 리눅스 서버에서 cat /proc/sys/kernel/msgmax가 65535 였다. 메시지 상한을 설정으로 요청하는 미들웨어가 이 값에 맞춰 줄이는 일이 흔하므로, 큰 메시지가 필요하면 먼저 이 값을 확인한다.

2-2. POSIX ✔

한도파일기본값비고
큐당 최대 메시지 수/proc/sys/fs/mqueue/msg_max10상한은 커널 버전에 따라 다름(Linux 3.5부터 65,536)
메시지 최대 크기/proc/sys/fs/mqueue/msgsize_max8,192바이트상한은 Linux 3.5부터 16,777,216
시스템 전체 큐 수/proc/sys/fs/mqueue/queues_max256상한 없음
사용자별 총 메모리RLIMIT_MSGQUEUE–그 사용자(real UID)가 가진 모든 큐가 쓰는 공간을 제한한다 ✔

POSIX 큐는 기본값이 작다

기본 msg_max=10이라서 11번째 메시지부터 송신이 블록된다. 처음 써보는 사람이 가장 많이 막히는 지점이다. 큐를 만들 때 mq_attr로 mq_maxmsg/mq_msgsize를 지정하되 위 한도를 넘지 못한다.

3. 수명과 정리 — 둘 다 “남는다”

System VPOSIX
프로세스가 종료하면큐는 그대로 남는다그대로 남는다 — “kernel persistence: if not removed by mq_unlink, a message queue will exist until the system is shut down” ✔
삭제msgctl(IPC_RMID) / 셸 ipcrm -qmq_unlink(name) / 셸 rm /dev/mqueue/name
남은 큐 찾기ipcs -qls /dev/mqueue
같은 key/이름으로 다시 만들기msgget(IPC_CREAT)는 기존 큐를 그대로 돌려준다. 새 큐를 원하면 IPC_EXCL(이미 있으면 EEXIST) ✔mq_open(O_CREAT)도 기존 큐를 연다. O_EXCL이면 실패

큐가 남아서 생기는 문제 (둘 다 해당)

  • 이전 메시지 잔재: 프로세스가 죽었다 살아나도 큐에는 죽기 전에 쌓인 메시지가 남아 있다. 재시작 시 큐를 삭제 후 재생성하는 구현이 흔한 이유다.
  • key/이름을 바꾸면 이전 큐가 고아가 된다: 새 key로 큐를 만들고 이전 key의 큐는 지우지 않으면 쓰이지 않는 큐가 쌓인다. 시스템 전체 한도(MSGMNI)를 소모하고, ipcs -q 출력을 혼란스럽게 한다.
  • 특히 System V는 큐를 만든 프로세스의 PID가 기록되지 않는다. ipcs -q -p는 마지막 송신자(lspid)와 수신자(lrpid) 만 보여준다. 공유메모리(ipcs -m -p의 cpid)와 다르다. ✔(msgget 페이지: 생성 시 msg_lspid, msg_lrpid는 0으로 설정)

4. 큐 적체를 어떻게 보는가

System VPOSIX
APImsgctl(IPC_STAT) → msqid_dsmq_getattr → mq_attr
현재 메시지 수msg_qnummq_curmsgs
현재 쌓인 바이트msg_cbytes(직접 필드 없음) △
큐 최대 바이트msg_qbytesmq_maxmsg × mq_msgsize
마지막 송신/수신 PIDmsg_lspid, msg_lrpid없음
셸ipcs -q (used-bytes, messages 열)cat /dev/mqueue/<name> (QSIZE)

→ “큐 사용률 %“를 계산할 수 있다: System V라면 msg_cbytes / msg_qbytes. 모니터링 도구가 큐 점유율 알람을 만들 때 쓰는 값이다. △

5. 한 큐 vs 수신자마다 큐 — 설계 선택

설계System V에서POSIX에서
수신자(프로세스)마다 큐 하나key 하나씩 배정이름 하나씩
큐 하나를 공유하고 타입으로 분배mtype으로 가능불가능 — 우선순위만 있음
이름으로 찾기표준에 없다. key라는 숫자만 있어서 “이름 → key” 표를 따로 만들어야 한다이름이 곧 식별자라서 표가 필요 없다

"이름-key 매핑 표 파일"이 생기는 이유

System V는 식별자가 숫자 key뿐이라 사람이 쓰는 이름과 key를 이어 줄 표가 필요하다. 이를 설정 파일로 두는 방식이 이름-키 매핑 테이블 방식의 IPC 키 관리다. POSIX라면 "/myproc"처럼 이름을 바로 쓰므로 그 표가 필요 없다. key 충돌을 피하는 ftok 쪽은 ftok과 IPC 키 생성.

6. 그래서 무엇을 쓰는가

상황선택
여러 UNIX(HP-UX, Solaris, AIX 등)에 이식해야 하는 오래된 코드System V — 어디에나 있다 △
epoll 이벤트 루프에 메시지 큐를 합치고 싶다 (Linux 전용)POSIX 메시지 큐
한 큐에서 수신자별로 골라 받기System V (mtype)
수신 타임아웃이 필요POSIX (mq_timedreceive)
큐 상태를 ipcs로 빠르게 점검System V
메시지 우선순위POSIX
이식성보다 단순함둘 다 가능. 새 코드는 보통 소켓/파이프를 먼저 고려 △

7. 환경 점검 명령 모음

ipcs -q                      # System V 큐 목록 (key, msqid, used-bytes, messages)
ipcs -q -p                   # 마지막 송신/수신 PID (생성자 PID 아님)
ipcs -q -l                   # System V 큐 한도 요약
cat /proc/sys/kernel/msgmax /proc/sys/kernel/msgmnb /proc/sys/kernel/msgmni
cat /proc/sys/fs/mqueue/msg_max /proc/sys/fs/mqueue/msgsize_max /proc/sys/fs/mqueue/queues_max
mount | grep mqueue          # POSIX 큐를 /dev/mqueue로 볼 수 있는지

8. 검증 상태 요약

항목상태근거
POSIX 큐 디스크립터는 Linux에서 fd, epoll 가능(이식 불가)✔mq_overview(7)
POSIX 한도 기본값(msg_max=10, msgsize_max=8192, queues_max=256), RLIMIT_MSGQUEUE✔mq_overview(7)
POSIX 우선순위 높은 것부터 전달, 범위 0~32767, mq_notify, 수명(mq_unlink)✔mq_overview(7)
System V msgtyp 규칙, IPC_NOWAIT 오류 코드, mtype>0, EINTR 재시작 안 됨✔msgrcv(2)
System V MSGMNI 기본 32,000(3.19~), msg_qbytes=MSGMNB, 생성 시 lspid/lrpid=0✔msgget(2)
System V 큐가 epoll에 못 들어감, MSGMAX 초과 시 EINVAL, 배포판별 msgmax 기본값, mq_timed* 세부△기억 기준. msgrcv(2) 페이지에 select/poll 언급이 없어 직접 확인하지 못함

관련 문서